#include "BloqueArbol.h"
#include <cmath>

#include "ManipuladorVector.h"
#include "Arbol.h"

BloqueArbol::BloqueArbol(Arbol* a, cantBytes tamanio,TipoClave* extractor) {

	this->tamanio = tamanio;
	this->modificado = false;
	this->borrado = false;
	this->arbol = a;
	this->cantClavesMax = a->getCantClavesMax();
	this->extractor = extractor;
}


BloqueArbol::~BloqueArbol() {

}
void BloqueArbol::setClaves(const std::vector<Key>& vector) {
	this->claves = vector;
}

void BloqueArbol::cambiarClave(Key claveAnterior, Key claveNueva) {
	this->setModificado();

	for (unsigned i = 0; i < this->claves.size(); i++) {
		Key clave = this->getClaves()[i];
		if (clave == claveAnterior) {
			this->claves[i] = claveNueva;
		}
	}
}


offset BloqueArbol::getId() {

	return this->id;
}

offset BloqueArbol::getPadre() {

	return this->padre;
}

vector<Key>& BloqueArbol::getClaves() {

	return this->claves;
}

vector<offset>& BloqueArbol::getPunteros() {

	return this->punteros;
}

void BloqueArbol::setPunteros(const vector<offset>& vector) {
	this->punteros = vector;
}

unsigned BloqueArbol::getCantClavesMax(){
	return cantClavesMax;
}
unsigned BloqueArbol::getCantClavesMin(){
	return floor((2*cantClavesMax)/3);


}
void BloqueArbol::actualizarClaves() {

	       unsigned cantPunteros = this->getPunteros().size();
	       if(cantPunteros != 0){
	       Key claveAux;
	       this->claves.clear();
	       BloqueArbol* temp = NULL;
	       for (unsigned p = 0; p < (cantPunteros-1); ++p) {
	               temp = this->arbol->get(this->punteros[p]);
	               this->claves.push_back( temp->getMayorDeRama() );
	       }
	       this->setModificado();
}}
void BloqueArbol::setId(offset id) {

	this->id = id;
}

void BloqueArbol::setPadre(offset padre) {
	this->padre = padre;
}

Bytes BloqueArbol::serializarClaves() {

	Bytes serializacionClaves;
	vector<Key>::iterator it =  this->claves.begin();

	/* tamanio 1 | clave 1 | tamanio 2 | clave 2 |..| tamanio n | clave n */

	for (; it != this->claves.end(); it++) {
		Key clave = (*it);
		cantBytes tamanio = clave.serializarse().getTamanio();

		serializacionClaves.agregarAlFinal(&tamanio,sizeof(cantBytes));;
		serializacionClaves.agregarAlFinal(clave.serializarse());
	}

	return serializacionClaves;
}

Bytes BloqueArbol::serializarPunteros() {

	Bytes serializacionBloques;
	vector<offset>::iterator it = this->punteros.begin();

	for ( ; it != this->punteros.end(); it++) {

		offset off = *it;
		serializacionBloques.agregarAlFinal(&off,sizeof(offset));
	}

	return serializacionBloques;
}

Bytes BloqueArbol::serializarse() {

	Bytes serializacion;

	serializacion.agregarAlFinal(&this->tamanio,sizeof(cantBytes));

	cantBytes cantClaves = this->claves.size();

	serializacion.agregarAlFinal(&cantClaves,sizeof(cantClaves));
	serializacion.agregarAlFinal(this->serializarClaves());

	cantBytes cantPunteros = this->punteros.size();

	serializacion.agregarAlFinal(&cantPunteros,sizeof(cantPunteros));
	serializacion.agregarAlFinal(this->serializarPunteros());

	return serializacion;
}

offset BloqueArbol::hidratarClaves(Bytes datosClaves,cantBytes cantClaves) {

	cantBytes cantClavesHidratadas = 0;
	offset offsetActual = 0;

	while (cantClavesHidratadas < cantClaves) {

		bool exito = true;

		cantBytes tamanioClaveActual;
		Bytes bytesClaveActual;

		exito = datosClaves.leer(&tamanioClaveActual,offsetActual,sizeof(cantBytes));

		if (!exito) {
			cerr << "Se intento hidratar bytes de claves invalidas" << endl;

			this->claves.clear();
			return 0;
		}

		offsetActual += sizeof(cantBytes);

		bytesClaveActual = datosClaves.getSubBytes(offsetActual,tamanioClaveActual);
		offsetActual += tamanioClaveActual;

		if (bytesClaveActual.getTamanio() == 0) {

			cerr << "Se intento hidratar bytes de claves invalidas" << endl;

			this->claves.clear();
			return 0;
		}

		this->claves.push_back(this->extractor->crearClave(bytesClaveActual));

		cantClavesHidratadas++;
	}

	if (cantClavesHidratadas != cantClaves) {
		cerr << "Se intento hidratar bytes de claves invalidas" << endl;
		return 0;
	}

	return offsetActual;
}

bool BloqueArbol::hidratarPunteros(Bytes datosPunteros) {

	if ((datosPunteros.getTamanio() % sizeof(offset)) != 0) {

		cerr << "Se intento hidratar un bloque con punteros daniados" << endl;
		return false;
	}

	cantBytes cantPunteros = datosPunteros.getTamanio() / sizeof(offset);

	/* Borro todo los punteros */
	this->punteros.clear();

	for (cantBytes i = 0 ; i < cantPunteros; i++) {

		offset offsetActual;
		datosPunteros.getSubBytes(i*sizeof(offset),sizeof(offset)).leer(&offsetActual,0,sizeof(offset));

		this->punteros.push_back(offsetActual);
	}

	return true;
}

bool BloqueArbol::hidratarse(const Bytes& bytes) {

	bytes.leer(&this->tamanio,0,sizeof(cantBytes));

	cantBytes cantidadClaves;
	bytes.leer(&cantidadClaves,sizeof(cantBytes),sizeof(cantidadClaves));

	Bytes claves = bytes.getSubBytes(sizeof(cantidadClaves) + sizeof(cantBytes), bytes.getTamanio() - sizeof(cantidadClaves) - sizeof(cantBytes));

	offset offsetPunteros = this->hidratarClaves(claves,cantidadClaves);

	if (offsetPunteros == 0) {
		return false;
	}

	cantBytes cantidadPunteros;
	claves.leer(&cantidadPunteros,offsetPunteros,sizeof(cantBytes));

	offsetPunteros += sizeof(cantBytes);

	Bytes bytesPunteros = claves.getSubBytes(offsetPunteros,cantidadPunteros*sizeof(offset));

	this->hidratarPunteros(bytesPunteros);

	return true;
}

void BloqueArbol::setTamanio(cantBytes tam){
	this->tamanio = tam;
}

void BloqueArbol::setModificado() {

	this->modificado = true;
}

bool BloqueArbol::getModificado() {

	return this->modificado;
}

bool BloqueArbol::existeOverflow(unsigned int cant2) {
	int cant1 = this->getClaves().size();
	unsigned mitad = (int) (cant1 + cant2) / 2;
	unsigned resto = (cant1 + cant2) % 2;
	if ((mitad + resto) > this->cantClavesMax) {
		return true;
	}
	return false;
}

bool BloqueArbol::existeUnderflow(unsigned int cant1) {

	unsigned cant2 = this->getClaves().size();
	unsigned mitad = (int) (cant1 + cant2) / 2;
	if (mitad < (floor((2 * cantClavesMax) / 3))) {
		return true;
	}
	return false;
}
unsigned BloqueArbol::getCantClaves(){
	return this->claves.size();
}

Key BloqueArbol::getClaveMayor() const {
	return this->claves[this->claves.size() - 1];
}

void BloqueArbol::setBorrado() {
	borrado = true;
	modificado = true;
}

bool BloqueArbol::getBorrado() {
	return borrado;
}

offset BloqueArbol::getAdan() {
	offset oPadre = this->getPadre();
	if(oPadre == ID_NULL){
		return this->getId();
	}

	BloqueArbol* padre = arbol->get(oPadre);
	return	padre->getAdan();
}

cantBytes BloqueArbol::calcularTamanio(cantBytes cantClaves, cantBytes tamMaximoClave) {

	cantBytes tamanio = 0;
	/* tamanio para almacenar el tamnio del bloque */
	tamanio += sizeof(cantBytes);

	/* tamanio para el siguiente del nodo hoja */
	tamanio += sizeof(offset);

	/* tamanio para la identificacion del tipo*/

	tamanio += sizeof(char);

	/* tamanio para almacenar el id */
	tamanio += sizeof(offset);

	/* tamanio para el padre*/
	tamanio += sizeof(offset);

	/* tamanio para la cantidad de claves*/

	tamanio += sizeof(unsigned);

	/* tamanio para almacenar las claves y sus id. de tamanio*/
	tamanio += (cantClaves*(tamMaximoClave + sizeof(offset)));

	/* tamanio para la cantidad de punteros*/

	tamanio += sizeof(cantBytes);

	/* tamanio para los punteros*/
	tamanio += (cantClaves + 1)*sizeof(offset);

	return tamanio;
}

cantBytes BloqueArbol::getTamanio() {

	return this->tamanio;
}

Arbol* BloqueArbol::getArbol(){
return arbol;
}

void BalancearDos(BloqueArbol* nodo, BloqueArbol* hermano, bool dosAUno){

std::vector<Key> a = nodo->getClaves();
		std::vector<Key> b = hermano->getClaves();
		if(dosAUno){
			ManipuladorVector::dosAUno<Key>(a, b);
		}else{
		ManipuladorVector::balancearVectores<Key>(a, b);
		}

		nodo->setClaves(a);
		hermano->setClaves(b);

		std::vector<offset> c = nodo->getPunteros();
		std::vector<offset> d = hermano->getPunteros();
if(dosAUno){
ManipuladorVector::dosAUno<offset>(c, d);
}else{
				ManipuladorVector::balancearVectores<offset>(c, d);
}
		nodo->setPunteros(c);
		hermano->setPunteros(d);

		nodo->setModificado();
		hermano->setModificado();

}


 void BalancearTres(BloqueArbol* hermanoIzq, BloqueArbol* nodo,
		BloqueArbol* hermano, bool tresADos){


std::vector<Key> a = hermanoIzq->getClaves();
	std::vector<Key> b = nodo->getClaves();
	std::vector<Key> c = hermano->getClaves();
	if (tresADos) {
		ManipuladorVector::balancearTresADos<Key>(a, b, c);
	} else {
		ManipuladorVector::balancearTres<Key>(a, b, c);
	}
	hermanoIzq->setClaves(a);
	nodo->setClaves(b);
	hermano->setClaves(c);

	hermano->setModificado();
	nodo->setModificado();
	hermanoIzq->setModificado();

	std::vector<offset> d = hermanoIzq->getPunteros();
	std::vector<offset> e = nodo->getPunteros();
	std::vector<offset> f = hermano->getPunteros();

	if (tresADos) {
		ManipuladorVector::balancearTresADos<offset>(d, e, f);
	} else {
		ManipuladorVector::balancearTres<offset>(d, e, f);
	}
	hermanoIzq->setPunteros(d);
	nodo->setPunteros(e);
	hermano->setPunteros(f);

}


void BloqueArbol::nuevosHijos(offset viejo){
 	BloqueArbol* temp = NULL;
 	unsigned size = this->getPunteros().size();
 	offset miId = getId();

 		for (unsigned i = 0; i < size; i++) {
 			if(this->getPunteros()[i] == id ){
 				this->punteros[i] = viejo;
 			}
 			temp = this->getArbol()->get(this->getPunteros()[i]);
 			temp->setPadre(miId);
 			temp->setModificado();
 		}
 }

void BloqueArbol::nuevosHijos(){
 	BloqueArbol* temp = NULL;
 	unsigned size = this->getPunteros().size();
 	offset miId = getId();
 		for (unsigned i = 0; i < size; i++) {
 		 			temp = this->getArbol()->get(this->getPunteros()[i]);
 			temp->setPadre(miId);
 			temp->setModificado();
 		}
 }



